package ftp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * FTP管理工具
 * 
 * @author 唐家林
 * @time 2014年10月28日
 */
public class FTPManageUtil {
	protected static Logger logger = LoggerFactory.getLogger(FTPManageUtil.class);
	/**
	 * 编码格式
	 */
	private static final String ENCODING = "UTF-8";

	/**
	 * 超时时间,2分钟
	 */
	private static int timeOut = 2 * 60 * 1000;
	
	/**
	 * 私有构造方法,不允许外部实例化
	 */
	private FTPManageUtil() {}

	/**
	 * 关闭FTP连接
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * @return 成功关闭返回true
	 */
	public static boolean closeConnect(FTPClient ftpClient) {
		try {
			if (ftpClient != null && !ftpClient.isConnected()) {
				ftpClient.logout();
				ftpClient.disconnect();
			}
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * 连接FTP服务器
	 * 
	 * @param userName
	 *            用户名
	 * @param password
	 *            密码
	 * @param ip
	 *            连接FTP的IP地址
	 * @param port
	 *            端口
	 * @return 连接成功返回FTP连接.当用户名、密码为空,IP为空、IP格式错误或连接失败返回null
	 */
	public static FTPClient connectServer(String userName, String password, String ip, int port) {
		// 用户名或密码为空退出
		if (null == userName || userName.isEmpty() || null == password || password.isEmpty()) {return null;}
		// IP不正确退出
		if (null == ip || !ip.matches("((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))))")) {
			return null;
		}

		FTPClient ftpClient = new FTPClient();
		try {
			ftpClient.setControlEncoding(ENCODING);

			ftpClient.setDefaultPort(port);
			ftpClient.connect(ip);
			ftpClient.login(userName, password);
			ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
			log("正在建立连接,连接地址:" + ip);

			ftpClient.setDataTimeout(timeOut);

			if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
				ftpClient.disconnect();
				ftpClient = null;
			}
		} catch (SocketException e) {
			log("登录ftp服务器 " + ip + " 失败,连接超时！");
			e.printStackTrace();
		} catch (IOException e) {
			log("登录ftp服务器 " + ip + " 失败，FTP服务器无法打开！");
			e.printStackTrace();
		}
		log("登陆ftp服务器成功:" + ip);
		return ftpClient;
	}

	/**
	 * 文件下载
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * @param remotePath
	 *            文件存放在服务器的文件路径
	 * @param localPath
	 *            下载后保存到本地的文件路径
	 * @return 服务器没有指定文件夹或下载失败返回false
	 */
	public static boolean downFile(FTPClient ftpClient, String remotePath, String localPath) {
		if (null == ftpClient) {
			return false;
		}
		File file = new File(localPath);
		// 如果其所在文件夹不存在则创建
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		boolean isSucceed = false;
		FileOutputStream os = null;
		try {
			File remoteFile = new File(remotePath);
			// FTP服务器目录跳转
			isSucceed = enterPath(ftpClient, remoteFile.getParent());
			// 如果目录跳转失败返回false
			if (!isSucceed) {
				return isSucceed;
			}
			// 重置成功状态
			isSucceed = false;
			String remoteName = remoteFile.getName();
			FTPFile[] fs = ftpClient.listFiles();
			for (FTPFile ff : fs) {
				if (ff.getName().equals(remoteName)) {
					// 文件名
					os = new FileOutputStream(localPath);
					isSucceed = ftpClient.retrieveFile(ff.getName(), os);
				}
			}
		} catch (IOException e) {
			isSucceed = false;
			log("文件下载异常!");
			e.printStackTrace();
		} finally {
			if (null != os) {
				try {
					os.close();
				} catch (IOException e) {}
			}
		}
		return isSucceed;
	}

	/**
	 * 进入指定目录
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * @param path
	 *            路径
	 * @return 成功进入返回true
	 */
	public static boolean enterPath(FTPClient ftpClient, String path) {
		if (null == path || path.isEmpty()) {
			log("路径为空!");
			return false;
		}
		// Windows系统下路径分隔符替换
		path = path.replaceAll("\\\\", "/");
		boolean isSucceed = false;
		try {
			isSucceed = ftpClient.changeWorkingDirectory(path);
		} catch (IOException e) {
			log("路径跳转失败!路径:" + path);
			isSucceed = false;
			e.printStackTrace();
		}
		return isSucceed;
	}

	public static FTPFile[] listRemoteFiles(FTPClient ftpClient) {
		FTPFile[] files = null;
		try {
			files = ftpClient.listFiles();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		return files;
	}

	/**
	 * 获得指定文件夹下的文件列表
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * @param path
	 *            路径
	 * @return 返回一个数组,该数组中每个元素都是一个文件
	 */
	public static FTPFile[] listRemoteFiles(FTPClient ftpClient, String path) {
		FTPFile[] files = null;
		try {
			files = ftpClient.listFiles(path);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		return files;
	}
	private static void log(String msg) {
		if (logger.isDebugEnabled()) {
			logger.debug(msg);
		}
	}

	/**
	 * 在当前目录创建文件夹或递归创建路径下包含的所有不存在的文件夹
	 * <pre>
	 * 如果是文件夹名称则在当前路径下创建该文件夹
	 * 如果是文件夹路径,则在创建该路径所有不存在的文件夹
	 * 如果文件夹名称中有"/"则,判断其为路径
	 * </pre>
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * 
	 * @param folderName
	 *            文件夹名称或路径.
	 * @return 操作成功返回true
	 */
	public static boolean mkdirs (FTPClient ftpClient, String folderName) {
		boolean isSuccess = false;
		try {
			// 先直接创建该文件夹,如果创建失败表面当前文件夹存在或父文件夹不存在
			isSuccess = ftpClient.makeDirectory(folderName);
			if (!isSuccess) { // 创建文件夹失败则创建父文件夹
				boolean parentSuccess = true;
				File file = new File(folderName);
				String parent = file.getParent().replaceAll("\\\\", "/");
				// 判断父文件夹下是否包含子,如果为true表示父文件夹下没有子文件或父文件夹不存在
				if (ftpClient.listFiles(parent).length == 0) {
					// 递归进入创建文件夹方法
					parentSuccess = mkdirs(ftpClient, parent);
				}
				if (parentSuccess) {
					isSuccess = ftpClient.makeDirectory(folderName);
				}
			}
		} catch (IOException e) {
			isSuccess = false;
			e.printStackTrace();
		}
		return isSuccess;
	}

	/**
	 * 移动文件或文件夹到指定路径
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * @param oldPath
	 *            希望移动的文件或文件夹路径
	 * @param newPath
	 *            文件或文件夹的新路径
	 * @return 操作成功返回true
	 */
	public static boolean move (FTPClient ftpClient, String oldPath, String newPath) {
		return rename(ftpClient, oldPath, newPath);
	}
	
	/**
	 * 重命名文件或目录
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * @param oldName
	 *            希望重命名的文件或目录名称
	 * @param newName
	 *            重命名后的新名称
	 * @return 操作成功返回true
	 */
	public static boolean rename (FTPClient ftpClient, String oldPath, String newPath) {
		boolean isSucceed = false;
		try {
			isSucceed = ftpClient.rename(oldPath, newPath);
		} catch (IOException e) {
			isSucceed = false;
			e.printStackTrace();
		}
		return isSucceed;
	}
	
	/**
	 * 文件上传
	 * 
	 * @param ftpClient
	 *            FTP连接
	 * @param remotePath
	 *            文件存放在服务器的文件路径
	 * @param localPath
	 *            本地文件路径
	 * @return 服务器没有指定文件夹或上传失败返回false
	 */
	public static boolean upFile(FTPClient ftpClient, String remotePath, String localPath) {
		if (null == ftpClient) {
			return false;
		}
		boolean isSucceed = false;
		FileInputStream in = null;
		try {
			File remoteFile = new File(remotePath);
			isSucceed = enterPath(ftpClient, remoteFile.getParent());
			// 如果目录跳转失败返回false
			if (!isSucceed) {
				return isSucceed;
			}
			// 重置成功状态
			isSucceed = false;
			File localFile = new File(localPath);
			in = new FileInputStream(localFile);
			isSucceed = ftpClient.storeFile(remoteFile.getName(), in);
		} catch (IOException e) {
			isSucceed = false;
			e.printStackTrace();
		} finally {
			if (null != in) {
				try {
					in.close();
				} catch (IOException e) {}
			}
		}
		return isSucceed;
	}

}
